// Filename: stl_compares.h
// Created by:  drose (28Sep04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#ifndef STL_COMPARES_H
#define STL_COMPARES_H

#include "dtoolbase.h"
#include "cmath.h"
#include "nearly_zero.h"
#include "addHash.h"

#include <assert.h>

#ifdef HAVE_STL_HASH
#include <hash_map>  // for hash_compare

template<class Key, class Compare = less<Key> >
class stl_hash_compare : public stdext::hash_compare<Key, Compare> {
public:
  INLINE bool is_equal(const Key &a, const Key &b) const {
    return !operator()(a, b) && !operator()(b, a);
  }
};

#else

#include <map>  // for less

// This is declared for the cases in which we don't have STL_HASH
// available.
template<class Key, class Compare = less<Key> >
class stl_hash_compare : public Compare {
public:
  INLINE size_t operator () (const Key &key) const {
    return (size_t)key;
  }
  INLINE bool operator () (const Key &a, const Key &b) const {
    return Compare::operator ()(a, b);
  }
  INLINE bool is_equal(const Key &a, const Key &b) const {
    return !operator()(a, b) && !operator()(b, a);
  }
};

#endif  // HAVE_STL_HASH

////////////////////////////////////////////////////////////////////
//       Class : floating_point_threshold
// Description : Compares two floating point numbers, within threshold
//               of equivalence.
////////////////////////////////////////////////////////////////////
template<class Key>
class floating_point_threshold {
public:
  INLINE floating_point_threshold(Key threshold = get_nearly_zero_value((Key)0));
  INLINE bool operator () (const Key &a, const Key &b) const;
  const Key _threshold;
};

////////////////////////////////////////////////////////////////////
//       Class : compare_to
// Description : An STL function object class, this is intended to be
//               used on any ordered collection of class objects that
//               contain a compare_to() method.  It defines the order
//               of the objects via compare_to().
////////////////////////////////////////////////////////////////////
template<class Key>
class compare_to {
public:
  INLINE bool operator () (const Key &a, const Key &b) const;
  INLINE bool is_equal(const Key &a, const Key &b) const;
};

////////////////////////////////////////////////////////////////////
//       Class : indirect_less
// Description : An STL function object class, this is intended to be
//               used on any ordered collection of pointers to classes
//               that contain an operator <() method.  It defines the
//               order of the pointers via operator <().
////////////////////////////////////////////////////////////////////
template<class Key>
class indirect_less {
public:
  INLINE bool operator () (const Key &a, const Key &b) const;
};

////////////////////////////////////////////////////////////////////
//       Class : indirect_compare_to
// Description : An STL function object class, this is intended to be
//               used on any ordered collection of pointers to classes
//               that contain a compare_to() method.  It defines the
//               order of the pointers via compare_to().
////////////////////////////////////////////////////////////////////
template<class Key>
class indirect_compare_to {
public:
  INLINE bool operator () (const Key &a, const Key &b) const;
  INLINE bool is_equal(const Key &a, const Key &b) const;
};

////////////////////////////////////////////////////////////////////
//       Class : indirect_compare_names
// Description : An STL function object class, this is intended to be
//               used on any ordered collection of pointers to classes
//               that define a get_name() method, particularly for
//               things that derive from Namable.  It defines the
//               order of the pointers by case-sensitive name
//               comparison.
////////////////////////////////////////////////////////////////////
template<class Key>
class indirect_compare_names {
public:
  INLINE bool operator () (const Key &a, const Key &b) const;
  INLINE bool is_equal(const Key &a, const Key &b) const;
};

////////////////////////////////////////////////////////////////////
//       Class : integer_hash
// Description : This is the default hash_compare class, which assumes
//               the Key is a size_t value or can be implicitly
//               converted to a size_t value (for instance, via a
//               size_t typecast operator).  It is the same as the
//               system-provided hash_compare.
////////////////////////////////////////////////////////////////////
template<class Key, class Compare = less<Key> >
class integer_hash : public stl_hash_compare<Key, Compare> {
public:
  INLINE static size_t add_hash(size_t start, const Key &key);
};

////////////////////////////////////////////////////////////////////
//       Class : pointer_hash
// Description : This is the default hash_compare class, which assumes
//               the Key is a pointer value.  It is the same as the
//               system-provided hash_compare.
////////////////////////////////////////////////////////////////////
class pointer_hash : public stl_hash_compare<const void *, less<const void *> > {
public:
  INLINE static size_t add_hash(size_t start, const void *key);
};

////////////////////////////////////////////////////////////////////
//       Class : floating_point_hash
// Description : This hash_compare class hashes a float or a double.
////////////////////////////////////////////////////////////////////
template<class Key>
class floating_point_hash : public stl_hash_compare<Key> {
public:
  INLINE floating_point_hash(Key threshold = get_nearly_zero_value((Key)0));
  INLINE size_t operator () (const Key &key) const;
  INLINE bool operator () (const Key &a, const Key &b) const;
  INLINE size_t add_hash(size_t start, const Key &key) const;
  const Key _threshold;
};

////////////////////////////////////////////////////////////////////
//       Class : sequence_hash
// Description : This hash_compare class hashes a string.  It assumes
//               the Key is a string or provides begin() and end()
//               methods that iterate through Key::value_type.
////////////////////////////////////////////////////////////////////
template<class Key, class Compare = less<Key> >
class sequence_hash : public stl_hash_compare<Key, Compare> {
public:
  INLINE size_t operator () (const Key &key) const;
  INLINE bool operator () (const Key &a, const Key &b) const {
    return stl_hash_compare<Key, Compare>::operator () (a, b);
  }
  INLINE static size_t add_hash(size_t start, const Key &key);
};

////////////////////////////////////////////////////////////////////
//       Class : method_hash
// Description : This hash_compare class hashes a class object.  It
//               assumes the Key provides a method called get_hash()
//               that returns a size_t.
////////////////////////////////////////////////////////////////////
template<class Key, class Compare = less<Key> >
class method_hash : public stl_hash_compare<Key, Compare> {
public:
  INLINE size_t operator () (const Key &key) const;
  INLINE bool operator () (const Key &a, const Key &b) const {
    return stl_hash_compare<Key, Compare>::operator () (a, b);
  }
};

////////////////////////////////////////////////////////////////////
//       Class : indirect_method_hash
// Description : This hash_compare class hashes a pointer to a class
//               object.  It assumes the Key is a pointer to a class
//               that provides a method called get_hash() that returns
//               a size_t.
////////////////////////////////////////////////////////////////////
template<class Key, class Compare>
class indirect_method_hash : public stl_hash_compare<Key, Compare> {
public:
  INLINE size_t operator () (const Key &key) const;
  INLINE bool operator () (const Key &a, const Key &b) const {
    return stl_hash_compare<Key, Compare>::operator () (a, b);
  }
};

////////////////////////////////////////////////////////////////////
//       Class : indirect_equals_hash
// Description : An STL function object class, this is intended to be
//               used on any ordered collection of pointers to classes
//               that contain an operator ==() method.  It defines
//               the equality of the pointers via operator ==().
//
//               Since it doesn't define the ordering of the pointers,
//               it can only be used with hash containers.
////////////////////////////////////////////////////////////////////
template<class Key>
class indirect_equals_hash {
public:
  INLINE size_t operator () (const Key &key) const;
  INLINE bool is_equal(const Key &a, const Key &b) const;
};

#include "stl_compares.I"

typedef floating_point_hash<float> float_hash;
typedef floating_point_hash<double> double_hash;
typedef integer_hash<int> int_hash;
typedef integer_hash<size_t> size_t_hash;
typedef sequence_hash<string> string_hash;
typedef sequence_hash<wstring> wstring_hash;

template<class Key>
class indirect_less_hash : public indirect_method_hash<Key, indirect_less<Key> > {
};

template<class Key>
class indirect_compare_to_hash : public indirect_method_hash<Key, indirect_compare_to<Key> > {
};

template<class Key>
class indirect_compare_names_hash : public indirect_method_hash<Key, indirect_compare_names<Key> > {
};

#endif
